#ifndef __LOGGER_H__
#define __LOGGER_H__

#include <atomic>
#include <mutex>
#include <cstdarg>
#include <cstdio>
#include <unordered_map>
#include "../util/utility.hpp"
#include "log_level.hpp"
#include "log_format.hpp"
#include "log_sink.hpp"
#include "../async_worker/async_worker.hpp"
#include "../util/handlerMessage.hpp"

namespace log_system
{
    // 日志器
    // 管理格式化日志器、管理日志接收器
    // 默认日志输出限制输出等级(大于默认等级才可输出)
    // 互斥锁(支持多线程下同步输出)
    // 不同等级日志的输出
    // 建造者模式(参数过多，构造日志器)
    class Logger
    {
    public:
        using pointer = std::shared_ptr<Logger>;

    public:
        Logger(const std::string &logger_name,
               log_system::LogLevel::Level limit_level,
               const log_system::Formatter::pointer &formatter,
               const std::vector<log_system::LogSink::pointer> &sinks)
            : _logger_name(logger_name), _limit_level(limit_level), _formatter(formatter), _sinks(sinks) {}

    public:
        const std::string &getLoggerName()
        {
            return _logger_name;
        }

    public:
        void debug(const std::string &file_name, size_t file_line, const std::string &format, ...)
        {
            if (log_system::LogLevel::Level::DEBUG < _limit_level) // 默认限制级别做判断
                return;
            va_list args_list; // 对format格式化字符串和不定参数组织得到日志消息字符串
            va_start(args_list, format);
            char *args_buffer;
            if (vasprintf(&args_buffer, format.c_str(), args_list) == -1)
            {
                HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::ERROR, "log-system/include/log/logger.hpp", __LINE__, "%s", "vasprintf failed!");
                return;
            }
            va_end(args_list);
            serialization(log_system::LogLevel::Level::DEBUG, file_name, file_line, args_buffer);
            free(args_buffer);
        }

        void info(const std::string &file_name, size_t file_line, const std::string &format, ...)
        {
            if (log_system::LogLevel::Level::INFO < _limit_level) // 默认限制级别做判断
                return;
            va_list args_list; // 对format格式化字符串和不定参数组织得到日志消息字符串
            va_start(args_list, format);
            char *args_buffer;
            if (vasprintf(&args_buffer, format.c_str(), args_list) == -1)
            {
                HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::ERROR, "log-system/include/log/logger.hpp", __LINE__, "%s", "vasprintf failed!");
                return;
            }
            va_end(args_list);
            serialization(log_system::LogLevel::Level::INFO, file_name, file_line, args_buffer);
            free(args_buffer);
        }

        void warning(const std::string &file_name, size_t file_line, const std::string &format, ...)
        {
            if (log_system::LogLevel::Level::WARNING < _limit_level) // 默认限制级别做判断
                return;
            va_list args_list; // 对format格式化字符串和不定参数组织得到日志消息字符串
            va_start(args_list, format);
            char *args_buffer;
            if (vasprintf(&args_buffer, format.c_str(), args_list) == -1)
            {
                HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::ERROR, "log-system/include/log/logger.hpp", __LINE__, "%s", "vasprintf failed!");
                return;
            }
            va_end(args_list);
            serialization(log_system::LogLevel::Level::WARNING, file_name, file_line, args_buffer);
            free(args_buffer);
        }

        void error(const std::string &file_name, size_t file_line, const std::string &format, ...)
        {
            if (log_system::LogLevel::Level::ERROR < _limit_level) // 默认限制级别做判断
                return;
            va_list args_list; // 对format格式化字符串和不定参数组织得到日志消息字符串
            va_start(args_list, format);
            char *args_buffer;
            if (vasprintf(&args_buffer, format.c_str(), args_list) == -1)
            {
                HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::ERROR, "log-system/include/log/logger.hpp", __LINE__, "%s", "vasprintf failed!");
                return;
            }
            va_end(args_list);
            serialization(log_system::LogLevel::Level::ERROR, file_name, file_line, args_buffer);
            free(args_buffer);
        }

        void fatal(const std::string &file_name, size_t file_line, const std::string &format, ...)
        {
            if (log_system::LogLevel::Level::FATAL < _limit_level) // 默认限制级别做判断
                return;
            va_list args_list; // 对format格式化字符串和不定参数组织得到日志消息字符串
            va_start(args_list, format);
            char *args_buffer;
            if (vasprintf(&args_buffer, format.c_str(), args_list) == -1)
            {
                HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::ERROR, "log-system/include/log/logger.hpp", __LINE__, "%s", "vasprintf failed!");
                return;
            }
            va_end(args_list);
            serialization(log_system::LogLevel::Level::FATAL, file_name, file_line, args_buffer);
            free(args_buffer);
        }

        void show()
        {
            for (int i = 0; i < _sinks.size(); ++i)
            {
                std::cout << "Logger -> " + _sinks[i]->show() << std::endl;
            }
        }

    private:
        // 序列化
        void serialization(log_system::LogLevel::Level level, const std::string &file_name, size_t file_line, char *args_buffer)
        {
            log_system::LogMessage message(level, file_name, file_line, _logger_name, args_buffer); // 构造LogMessage对象
            std::stringstream ss;                                                                   // 拿到日志字符串
            _formatter->format(ss, message);                                                        // 格式化
            log(ss.str().c_str(), ss.str().size());                                                 // 不同输出位置输出
        }

    protected:
        virtual void log(const char *data, size_t length) = 0;

    protected:
        std::string _logger_name;                              // 日志器名称
        std::atomic<log_system::LogLevel::Level> _limit_level; // 默认限制级别
        log_system::Formatter::pointer _formatter;             // 格式化器
        std::vector<log_system::LogSink::pointer> _sinks;      // 存储多个接收器
        std::mutex _mutex;                                     // 互斥锁(保证多线程同步)
    };

    class SyncLogger : public Logger // 同步日志器
    {
    public:
        SyncLogger(const std::string &logger_name,
                   log_system::LogLevel::Level limit_level,
                   const log_system::Formatter::pointer &formatter,
                   const std::vector<log_system::LogSink::pointer> &sinks)
            : Logger(logger_name, limit_level, formatter, sinks) {}

    public:
        void log(const char *data, size_t length) override
        {
            std::unique_lock<std::mutex> lock(_mutex); // 加锁
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(data, length);
            }
        }
    };

    // 异步日志器
    // 前提 : 业务线程->缓冲区->目标位置、缓冲区(共享资源:需要确保线程安全)、缓冲区设计: 线性表(生产消费模型)
    // 后知 : 问题: 线程互斥一次一条日志写入logSink效率太慢、解决方案: 双缓冲区(业务线程->任务写入缓冲区->任务处理缓冲区->异步工作线程(目标位置)) -> 降低生产者和消费者之间的锁冲突
    class AsyncLogger : public Logger // 异步日志器
    {
    public:
        AsyncLogger(const std::string &logger_name,
                    log_system::LogLevel::Level limit_level,
                    const log_system::Formatter::pointer &formatter,
                    const std::vector<log_system::LogSink::pointer> &sinks)
            : Logger(logger_name, limit_level, formatter, sinks),
              _async_worker(std::make_shared<AsyncWorker>(std::bind(&AsyncLogger::logSink, this, std::placeholders::_1)))
        {
            // for (int i = 0; i < _sinks.size(); ++i)
            // {
            //     std::cout << _sinks[i]->show() << std::endl;
            // }
        }

    public:
        void log(const char *data, size_t length) override
        {
            // std::cout << "void log(const char *data, size_t length) override" << std::endl;
            _async_worker->write(data, length);
        }

        void logSink(log_system::Buffer &buffer) // 无需加锁: 日志工作器中对缓冲区进行条件变量限制和加锁临界资源保证串行化
        {
            if (_sinks.empty())
            {
                log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "%s", "AsyncLogger.logSink() -> _sinks is empty, please use LogSinkSet class to set sinks!");
                return;
            }
            for (int i = 0; i < _sinks.size(); ++i)
            {
                _sinks[i]->log(buffer.readableBeginPointer(), buffer.readableLength());
            }
        }

    private:
        log_system::AsyncWorker::pointer _async_worker; // 异步工作器
    };

    /* 建造者模式建造日志器(降低用户使用复杂度: test_logger.cc使用日志器方法太复杂) */
    class LoggerBuilder
    {
    public:
        enum Type // 日志器类型
        {
            LOGGER_SYNC, // 同步日志器
            LOGGER_ASYNC // 异步日志器
        };

    public:
        LoggerBuilder()
            : _logger_type(Type::LOGGER_SYNC),
              _limit_level(log_system::LogLevel::Level::UNKNOWN) {}

    public:
        void buildLoggerType(const Type &logger_type)
        {
            _logger_type = logger_type;
#ifdef DEBUG_HANDLER_MESSAGE
            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "LoggerBuilder._logger_type: %d", _logger_type);
#endif
        }

        void buildLoggerName(const std::string &logger_name)
        {
            _logger_name = logger_name;
#ifdef DEBUG_HANDLER_MESSAGE

            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "LoggerBuilder._logger_name: %s", _logger_name.c_str());
#endif
        }

        void buildLoggerLevel(const LogLevel::Level &limit_level)
        {
            _limit_level = limit_level;
#ifdef DEBUG_HANDLER_MESSAGE

            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "LoggerBuilder._limit_level: %d", _limit_level);
#endif
        }

        void buildFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<log_system::Formatter>(pattern);
#ifdef DEBUG_HANDLER_MESSAGE

            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "LoggerBuilder._formatter: %p", _formatter.get());
#endif
        }

        template <typename SinkType, typename... Args>
        void buildLoggerSink(Args &&...args)
        {
            log_system::LogSink::pointer log_sink_pointer = log_system::LogSinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(log_sink_pointer);
        }

        void show()
        {
            for (int i = 0; i < _sinks.size(); ++i)
            {
                log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "LoggerBuilder--> %s", _sinks[i]->show().c_str());
            }
        }

        virtual Logger::pointer build() = 0; // 创建日志器

    protected:
        Type _logger_type;
        std::string _logger_name;
        log_system::LogLevel::Level _limit_level;
        log_system::Formatter::pointer _formatter;
        std::vector<LogSink::pointer> _sinks;
    };

    // 通过类型控制同步日志器还是异步日志器输出
    class LocalLoggerBuilder : public LoggerBuilder // 局部日志器建造器
    {
    public:
        Logger::pointer build() override
        {
            assert(!_logger_name.empty());
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<log_system::Formatter>();
            }
            if (_sinks.empty())
            {
                buildLoggerSink<StdoutLogSink>();
            }
            if (_logger_type == LoggerBuilder::Type::LOGGER_ASYNC) // 异步日志器
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    // 日志器管理器
    // 单例模式(全局访问)
    // 提供给GlobalLoggerBuilder使用
    class LoggerManager
    {
    private:
        LoggerManager()
        {
            // 注意 : builder只能new局部日志器建造者, 否则new全局日志器建造者调用build()会死循环阻塞
            std::unique_ptr<log_system::LoggerBuilder> builder(new log_system::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers.insert(std::make_pair("root", _root_logger));
#ifdef DEBUG_HANDLER_MESSAGE
            show();
            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "%s", "LoggerManager construct done!!!");
#endif
        }

    public:
        static LoggerManager &getInstance()
        {
            static LoggerManager logger_manager; // static资源只有一份, C++11之后静态局部变量, 编译层面实现线程安全
#ifdef DEBUG_HANDLER_MESSAGE
            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::INFO, "log-system/include/log/logger.hpp", __LINE__, "%s", "log_system::LoggerManager::getInstance() called");
#endif
            return logger_manager;
        }

    public:
        void addLogger(const log_system::Logger::pointer &logger)
        {
            if (haveLogger(logger->getLoggerName()))
            {
                HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::WARNING, "log-system/include/log/logger.hpp", __LINE__, "%s", "had logger, please reset the logger_name option in the constructor of the LoggerDirector class ");
                return;
            }
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _loggers.insert(std::make_pair(logger->getLoggerName(), logger));
            }
#ifdef DEBUG_HANDLER_MESSAGE
            HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::INFO, "log-system/include/log/logger.hpp", __LINE__, "%s", "add logger done");
#endif
        }

        bool haveLogger(const std::string &logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            std::unordered_map<std::string, Logger::pointer>::const_iterator it = _loggers.find(logger_name);
            if (it == _loggers.end())
                return false;
            return true;
        }

        Logger::pointer getLogger(const std::string &logger_name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            std::unordered_map<std::string, Logger::pointer>::const_iterator it = _loggers.find(logger_name);
            if (it == _loggers.end())
                return Logger::pointer();
            return it->second;
        }

        Logger::pointer defalutLogger()
        {
            return _root_logger;
        }

        void show()
        {
            for (auto &it : _loggers)
            {
                log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::DEBUG, "log_system/include/log/logger.hpp", __LINE__, "%s : %p", it.first.c_str(), it.second.get());
            }
        }

    private:
        std::mutex _mutex;                                         // 互斥锁
        Logger::pointer _root_logger;                              // 默认日志器
        std::unordered_map<std::string, Logger::pointer> _loggers; // 管理日志器数组
    };

    class GlobalLoggerBuilder : public LoggerBuilder // 全局日志器建造器
    {
    public:
        Logger::pointer build() override
        {
            assert(!_logger_name.empty());
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<log_system::Formatter>();
            }
            if (_sinks.empty())
            {
                buildLoggerSink<StdoutLogSink>();
            }
            Logger::pointer logger;
            if (_logger_type == LoggerBuilder::Type::LOGGER_ASYNC) // 异步日志器
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            else // 同步日志器
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::getInstance().addLogger(logger);
#ifdef DEBUG_HANDLER_MESSAGE
            HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::INFO, "log-system/include/log/logger.hpp", __LINE__, "%s", "build done!!!");
#endif
            return logger;
        }
    }; // class GlobalLoggerBuilder

    // 建造者使用成本过高 -> 指挥者提供相应接口给用户 -> TODO

    // 选择 局部日志器建造器/全局日志器建造器？
    // 构造日志器类型 同步日志器/异步日志器
    // 选择日志器限制类型
    // 设置日志器名称
    // 设置日志器格式化规则字符串
    // 设置日志器接收方式
    // 异步日志器是否设置非安全异步工作器

    // 引出 :
    // 由于LogSinkFactory类中create函数是模板函数需要传进类型和参数致使得LoggerBuilder类中的buildLoggerSink也是模板函数需要传进类型和参数,
    // 导致LoggerDirator构造无法通过参数来buildLoggerSink
    // 另外这里接收方式可以是多个，导致无法构造函数中使用单一参数来传递
    // 问题 : 如何将多种接收方式传进LoggerDireator
    // 采用LogSinkSet类完成对sink的builder

    class LoggerDirector
    {
    public:
        enum BuilderCategory
        {
            LOCAL_BUILDER,
            GLOBAL_BUILDER
        };

    protected:
        std::shared_ptr<log_system::LoggerBuilder> _logger_builder; // 日志建造器种类(可拷贝/赋值)
        log_system::Logger::pointer _logger;                        // 日志器

    public:
        LoggerDirector(const std::string &logger_name,
                       const LoggerDirector::BuilderCategory &builder_category = BuilderCategory::LOCAL_BUILDER,
                       const LoggerBuilder::Type &logger_type = LoggerBuilder::Type::LOGGER_SYNC,
                       const log_system::LogLevel::Level &limit_level = log_system::LogLevel::Level::UNKNOWN,
                       const std::string &pattern = "[%p][%d{%H:%M:%S}][%f:%l][%t][%c]%T%m%n")
        {
            assert(setLoggerBuiler(builder_category));
            _logger_builder->buildLoggerType(logger_type);
            _logger_builder->buildLoggerLevel(limit_level);
            _logger_builder->buildLoggerName(logger_name);
            _logger_builder->buildFormatter(pattern);
#ifdef DEBUG_HANDLER_MESSAGE
            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "%s", "LoggerDirector construct done!!!");
#endif
        }

        ~LoggerDirector() {}

        log_system::Logger::pointer create() // Pass the set log receiving mode, Logger name, restriction level, and formatter to the Logger class
        {
            _logger = _logger_builder->build();
            return _logger;
        }

        // ~LoggerDirector() = default;

        log_system::Logger::pointer getLogger() { return _logger; }
        std::shared_ptr<log_system::LoggerBuilder> &getLoggerBuilder() { return _logger_builder; }

    private:
        bool setLoggerBuiler(BuilderCategory builder_category)
        {
            if (builder_category == LOCAL_BUILDER)
            {
                _logger_builder = std::make_shared<log_system::LocalLoggerBuilder>();
            }
            else
            {
                _logger_builder = std::make_shared<log_system::GlobalLoggerBuilder>();
            }
            if (_logger_builder == nullptr)
                return false;
#ifdef DEBUG_HANDLER_MESSAGE
            log_system::HandlerMessage::handlerMessage(log_system::HandlerMessage::HandlerLevel::DEBUG, "log-system/include/log/logger.hpp", __LINE__, "%s : %p", "LoggerDirector._logger_builder", _logger_builder.get());
#endif
            return true;
        }

    public:
    }; // class LoggerDirector

    class LogSinkSet
    {
    private:
        std::shared_ptr<log_system::LoggerBuilder> _logger_builder;

    public:
        LogSinkSet(const std::shared_ptr<log_system::LoggerBuilder> &logger_builder)
        {
            _logger_builder = logger_builder;
        }
        ~LogSinkSet() {}

        inline void set(const log_system::StdoutLogSink &sink_category)
        {
            _logger_builder->buildLoggerSink<log_system::StdoutLogSink>();
        }

        inline void set(const log_system::SpecifyFileLogSink &sink_category, const std::string &file_path)
        {
            _logger_builder->buildLoggerSink<log_system::SpecifyFileLogSink>(file_path);
        }

        inline void set(const log_system::ScrollFileBySizeLogSink &sink_category, const std::string &base_filename, const size_t &maxsize)
        {
            _logger_builder->buildLoggerSink<log_system::ScrollFileBySizeLogSink>(base_filename, maxsize);
        }

        inline void set(const log_system::ScrollFileByTimeLogSink &sink_category, const std::string &base_filename, const log_system::ScrollFileByTimeLogSink::TimeGap &tiem_gap)
        {
            _logger_builder->buildLoggerSink<log_system::ScrollFileByTimeLogSink>(base_filename, tiem_gap);
        }
    }; // struct LogSinkSet
} // namespace log_system
#endif // #ifndef __LOGGER_H__
